#include "function.h"

#include <cmath>
#include <string>
#include <stdlib.h>
#include <vector>
#include <algorithm>

using std::string;

Function* Build (string const & s)
{	
	size_t depth = 0;
	size_t min_p = (size_t)-1;
	size_t min_i = 0;
	for (size_t i = 0; i < s.length(); ++i)
	{
		size_t p = 0;
		if (s[i] == '(')
			++depth;
		else if (s[i] == ')')
			--depth;
		
		else if ((s[i] == '+') || (s[i] == '-'))
			p = 4 * depth + 1;	
		else if ((s[i] == '*') || (s[i] == '/'))
			p = 4 * depth + 2;
		else if (s[i] == '^')
			p = 4 * depth + 3;
		else if ((s.substr(i, 3) == "sin") || (s.substr(i, 3) == "cos"))
			p = 4 * depth + 4;

		if (p != 0 && min_p > p)
		{
			min_p = p;
			min_i = i;
		}
	}

	size_t const brackets = min_p == 0 ? 0 : (min_p - 1) / 4;
    Function * ans = 0;
	if (min_i != 0)
    {
        Function* left  = Build(s.substr(brackets, min_i));
        Function* right = Build(s.substr(min_i + 1, s.length() - brackets));
        if (s[min_i] == '+')
        {
            ans = new Plus(left, right);
        }
	    else if(s[min_i] == '-')
        {
            ans = new Minus(left, right);
        }
        else if(s[min_i] == '*')
        {
			ans = new Mult(left, right);
        }
		else if(s[min_i] == '/')
        {
			ans = new Div(left, right);
        } 
		else if (s[min_i] == '^')
        {
			ans = new Power(left, right);
        }
	}
	else if ((s.substr(min_i, 3) == "sin"))
    {
		Function* right = Build(s.substr(min_i + 3, s.length() - brackets));
		ans = new Sin_(right);
    }
	else if ((s.substr(min_i, 3) == "cos"))
    {
		Function* right = Build(s.substr(min_i + 3, s.length() - brackets));
		ans = new Cos_(right);
    }
	else 
	{
		string ns(s);
		ns.erase(std::remove(ns.begin(), ns.end(), ')'), ns.end());
		ns.erase(std::remove(ns.begin(), ns.end(), '('), ns.end());
		if (ns == "x")
			ans = new Var();
		else
			ans = new Const(atof(ns.c_str()));
    }
    return ans;
}



Mult :: Mult(Function * a, Function * b)
{
       a_ = a;
       b_ = b;
}

double Mult :: value(double x) const
{
       return (a_->value(x) * b_->value(x));
}

Div :: Div(Function * a, Function * b)
{
       a_ = a;
       b_ = b;
}

double Div :: value(double x) const
{
       return (a_->value(x) / b_->value(x));
}

Plus :: Plus(Function * a, Function * b)
{
       a_ = a;
       b_ = b;
}

double Plus :: value(double x) const
{
       return (a_->value(x) + b_->value(x));
}

Minus :: Minus(Function * a, Function * b)
{
       a_ = a;
       b_ = b;
}

double Minus :: value(double x) const
{
       return (a_->value(x) - b_->value(x));
}

Power :: Power(Function * a, Function * b)
{
       a_ = a;
	   b_ = b;
}

double Power :: value(double x) const
{
       return (pow(a_->value(x), b_->value(x)));
}


Sin_ :: Sin_(Function * a)
{
       a_ = a;
}

double Sin_ :: value(double x) const
{
       return sin(a_->value(x));
}

Cos_ :: Cos_(Function * a)
{
       a_ = a;
}


double Cos_ :: value(double x) const
{
       return cos(a_->value(x));
}

double Var :: value(double x) const
{
       return x;
}

Const :: Const (double d)
{
       c = d;
}

double Const :: value(double x) const
{
       return c;
}
